# 公司：安徽银杰
# 作者：李伟

# 上节课学习的xpath定位方法是利用元素本身的特征 ，但是有可能还是找不到元素。
# 所以，需要借助层级关系来定位，也就是通过他们不同的父级或父级的父级来定位，或者通过兄弟姐妹来定位。


# 1、按照html页面的顺序，从上到下定位。--层级定位
# 写法：//.......//.......//.....
# 第一个//, 在整个html当中从上到下去查找
# 第二个//, 以第一个//找到的元素为根元素，在它的子孙后代当中去找。
# 注意如果是一个/ 就是儿子 不是所有的后代。
# 示例： //div[@class="number"]//p

# 2、轴定位
'''
什么情况下用到轴定位：如果元素之间有父子关系（儿子找爸爸）、兄弟关系（找哥哥/找弟弟）

从子孙元素，倒着找父元素或者祖先元素。
从兄弟姐妹元素，顺着关系找到其它的兄弟姐妹。
关系疏理：要找的元素，是已知元素的 xxx 关系。
'''
import time

# parent      选取当前节点的父节点                        (标黑)
# preceding-sibling  选取当前节点之前的所有兄弟节点--找哥哥  (标黑)
# following-sibling  选取当前节点之后的所有兄弟节点--找弟弟  (标黑)
# following   选取当前节点之后的所有节点
# ancestor    选取当前节点的所有祖先节点(包括父节点)
# preceding   选取当前节点之前的所有节点

'''
<!DOCTYPE html>
<html>
<head>
    <title>XPath 轴测试</title>
</head>
<body>
    <div id="root">
        <h1>文章标题</h1>
        
        <div class="section" id="section1">
            <h2>第一部分</h2>
            <p>这是第一部分的介绍。</p>
            <ul>
                <li id="target">目标列表项</li>
                <li>下一个列表项 (弟弟)</li>
                <li>最后一个列表项 (弟弟)</li>
            </ul>
            <p>这是第一部分的总结。</p>
        </div>
        
        <div class="section" id="section2">
            <h2>第二部分</h2>
            <p>这是第二部分的内容。</p>
        </div>
        
        <footer>文章结束</footer>
    </div>
</body>
</html>
'''

# XPath 轴详解与案例
# 假设我们的起点 XPath 是 //li[@id="target"]。

# 1. parent：选取当前节点的父节点 (标黑)

# 描述：直接返回当前节点的上一级节点。
# XPath 示例：//li[@id="target"]/parent::*

# 结果：
# <ul>
#     <li id="target">目标列表项</li>
#     <li>下一个列表项 (弟弟)</li>
#     <li>最后一个列表项 (弟弟)</li>
# </ul>
# 解释：target 列表项的直接父元素是 <ul>

# 2. preceding-sibling：选取当前节点之前的所有兄弟节点 (找哥哥)  (标黑)
# 描述：返回与当前节点拥有相同父节点，并且在文档顺序中位于当前节点之前的所有节点。
# XPath 示例：//li[@id="target"]/preceding-sibling::*
# 结果：空
# 解释：在 <ul> 中，target 列表项是第一个 <li>，它没有任何在它之前的兄弟节点。
# 如果我们以第二个 <li> 为目标 (//li[contains(text(), "下一个")])，它的 preceding-sibling 就是 <li id="target">目标列表项</li>。

# 3. following-sibling：选取当前节点之后的所有兄弟节点 (找弟弟)  (标黑)
# 描述：返回与当前节点拥有相同父节点，并且在文档顺序中位于当前节点之后的所有节点。
# XPath 示例：//li[@id="target"]/following-sibling::*
# 结果：

# <li>下一个列表项 (弟弟)</li>
# <li>最后一个列表项 (弟弟)</li>
# 解释：在 <ul> 中，target 后面的两个 <li> 都是它的弟弟。

# 4. following：选取当前节点之后的所有节点
# 描述：返回在文档顺序中位于当前节点之后的所有节点，不包括后代节点、属性节点和命名空间节点。可以理解为从当前节点结束到文档末尾的所有 “其他” 节点。
# XPath 示例：//li[@id="target"]/following::*
# 结果 (部分关键节点)：
# <li>下一个列表项 (弟弟)</li>
# <li>最后一个列表项 (弟弟)</li>
# </ul> (父节点的结束标签之后的内容)
# <p>这是第一部分的总结。</p>
# <div class="section" id="section2">...</div> (整个第二部分)
# <footer>文章结束</footer>
# 解释：这个轴也非常强大，它会选取 target 节点关闭之后的所有内容。

# 5. ancestor：选取当前节点的所有祖先节点 (包括父节点)
#
# 描述：返回当前节点的父节点、祖父节点、曾祖父节点，一直到文档的根节点。
# XPath 示例：//li[@id="target"]/ancestor::*
# 结果 (按从近到远的顺序)：
# <ul>...</ul> (父节点)
# <div class="section" id="section1">...</div> (祖父节点)
# <div id="root">...</div> (曾祖父节点)
# <body>...</body>
# <html>...</html>
# 解释：这个轴非常适合在你找到一个具体元素后，向上查找它所在的大的模块或容器。

# 6. preceding：选取当前节点之前的所有节点
# 描述：返回在文档顺序中位于当前节点之前的所有节点，不包括祖先节点、属性节点和命名空间节点。可以理解为从文档开头到当前节点前的所有 “其他” 节点。
# XPath 示例：//li[@id="target"]/preceding::*
# 结果 (部分关键节点)：
# <h1>文章标题</h1>
# <div class="section" id="section1">
# <h2>第一部分</h2>
# <p>这是第一部分的介绍。</p>
# <ul> (它的父节点，因为 <ul> 在 <li> 之前开始)
# 解释：这个范围非常广，它包括了所有在 target 节点出现之前的、不属于它自身层级结构的节点。
""" 
总结与记忆技巧
轴 (Axis)	        英文含义	      查找方向	     查找范围	                    记忆技巧
parent	            父亲	            向上	         直接父节点	                      "爸"，只有一个
preceding-sibling	之前的兄弟	    向前	         拥有相同父节点的、之前的节点	      "哥哥们"
following-sibling	之后的兄弟	    向后	         拥有相同父节点的、之后的节点	      "弟弟们"

following	        之后的	        向后	         文档中所有在它之后的节点 (除后代等)	  "后面的所有东西"，范围广
ancestor	        祖先	            向上	         所有祖先 (父，祖父，...)	          "祖宗十八代"，一直往上
preceding	        之前的	        向前	         文档中所有在它之前的节点 (除祖先等)    "前面的所有东西"，范围广

核心区别：
sibling 结尾的轴（preceding-sibling, following-sibling）只在同一层级（兄弟节点）中查找。
不带 sibling 的轴（preceding, following）在整个文档范围中查找，只要位置在它之前或之后。
parent 和 ancestor 是向上查找层级关系。

"""

# 案例：百度
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("http://testingpai.com/")

# 登录节点为基准
# driver.find_element(By.XPATH,"//span[@id='navLogin']").click()
# time.sleep(5)

# 1、parent	直接父节点
""" 
try:
    # 定位父节点元素
    parent_element = driver.find_element(By.XPATH, "//span[@id='navLogin']/parent::*")
    # 打印父节点的标签名
    print("父节点的标签名", parent_element)
    # 打印父节点的文本内容（如果有）
    print("父节点的文本内容：", parent_element.text)
    # 打印父节点的某个属性值（比如 class 属性，可根据实际情况修改）
    class_attribute = parent_element.get_attribute("class")
    print("父节点的 class 属性值：", class_attribute)

    time.sleep(5)
except Exception as e:
    print('定位或打印过程中出现错误', e)
    raise
finally:
    # 关闭浏览器
    driver.quit()


# 2、preceding-sibling  之前的兄弟  向前	拥有相同父节点的、之前的节点	     "哥哥们"

try:
    preceding_element = driver.find_element(By.XPATH, "//span[@id='navRegister']/preceding-sibling::*")
    # 由于使用登录的当前节点属于第一个节点，上面没有节点，打印会出错，所以使用注册定位。
    print("打印父节点相同的节点，哥哥们", preceding_element)

    # 打印父节点的文本内容（如果有）
    print("父节点的文本内容：", preceding_element.text)

    class_attribute = preceding_element.get_attribute("class")
    print("父节点的 class 属性值：", class_attribute)
    time.sleep(2)
except Exception as e:
    print("打印或定位过程中出错", e)
    raise
finally:
    driver.quit()


# 3、following-sibling  之后的兄弟	 向后 拥有相同父节点的、之后的节点	 "弟弟们"
try:
    following_element= driver.find_element(By.XPATH,"//span[@id='navLogin']/following-sibling::*")
    print("父节点的标签名",following_element)
    print("父节点的文本内容：", following_element.text)
    class_attribute = following_element.get_attribute("class")
    print("父节点的 class 属性值：", class_attribute)
    time.sleep(5)
except Exception as e:
    raise
finally:
    driver.quit()
"""

# 4、following当前节点之后的所有节点内容
following = driver.find_element(By.XPATH, "//span[@id='navLogin']/following::*")
print("父节点的标签名", following)
print("父节点的文本内容：", following.text)
class_attribute = following.get_attribute("class")
print("父节点的 class 属性值：", class_attribute)

# 4、preceding当前节点之前的所有节点内容（除祖先等）
# <div class="grandparent">  <!-- 祖父节点 -->
#   <div class="parent">     <!-- 父节点 -->
#     <span id="current">当前节点</span>  <!-- 当前节点 -->
#   </div>
#   <p>这是父节点之后的 p 节点</p>
# </div>
# <p>这是祖父节点之后的 p 节点</p>

# preceding 轴会选取在文档顺序上，位于 <span id="current"> 之前的节点，比如 class="parent" 的 <div> 里，
# 在 <span id="current"> 之前可能有的节点，以及更前面的节点；但不会选取 class="parent" 的 <div>（父节点）、
# class="grandparent" 的 <div>（祖父节点）这些祖先节点。

# 简单来说，preceding 关注的是 “文档书写顺序上的前后”，而祖先节点是 “层级包含关系上的上层”，所以 preceding 不会把祖先节点算进去，只选文档里写在当前节点前面、
# 且不是它祖先的那些节点。

# 5、ancestor   所有祖先 (父，祖父，...)	          "祖宗十八代"，一直往上

#
# //span[@id="navLogin"]/following-sibling::span[@id="navRegister"]